#! /usr/bin/env perl
##
## Copyright (C) by Argonne National Laboratory
##     See COPYRIGHT in top-level directory
##

# Class mapping
%ShortClassName = ( "Info" => "info",
		    "Win" => "win",
		    "Request" => "req",
		    "Comm" => "comm",
		    "Datatype" => "dtype",
		    "Intercomm" => "inter",
		    "Intracomm" => "intra",
		    "Op" => "op",
		    "File" => "file", 
		    "Grequest" => "greq",
		    "Errhandler" => "errh",
		    "Status" => "st",
		    "base" => "",
		    "Prequest" => "preq",
		    "Group" => "group",
		    "Cartcomm" => "cart",
		    "Graphcomm" => "graph",
		    );
%SkipRoutines = ( "Intracomm-Spawn" => 1, 
		  "Intracomm-Spawn_multiple" => 1,
		  "base-Init_thread" => 1,
		  "base-Init" => 1,
		  );
# These functions are primarily from MPI-1, and follow a different rule.
# ?? Should these have a place in the definition file instead of here?
%FuncHasClassAtEnd = ( 
		       "comm-Send" => 1,
		       "comm-Ssend" => 1,
		       "comm-Rsend" => 1,
		       "comm-Bsend" => 1,
		       "comm-Isend" => 1,
		       "comm-Issend" => 1,
		       "comm-Irsend" => 1,
		       "comm-Ibsend" => 1,
		       "comm-Irecv" => 1,
		       "comm-Send_init" => 1,
		       "comm-Bsend_init" => 1,
		       "comm-Rsend_init" => 1,
		       "comm-Ssend_init" => 1,
		       "comm-Recv_init" => 1,
		       "dtype-Create_contiguous" => 1,
		       "dtype-Create_vector" => 1,
		       "dtype-Create_indexed" => 1,
		       "inter-Gather" => 1,
		       "inter-Allgather" => 1,
		       "inter-Scatter" => 1,
		       "inter-Alltoall" => 1,
		       "inter-Gatherv" => 1,
		       "inter-Allgatherv" => 1,
		       "inter-Scatterv" => 1,
		       "inter-Alltoallv" => 1,
		       "inter-Allreduce" => 1,
		       "inter-Reduce" => 1,
		       "inter-Bcast" => 1,
		       "intra-Gather" => 1,
		       "intra-Allgather" => 1,
		       "intra-Scatter" => 1,
		       "intra-Alltoall" => 1,
		       "intra-Gatherv" => 1,
		       "intra-Allgatherv" => 1,
		       "intra-Scatterv" => 1,
		       "intra-Alltoallv" => 1,
		       "intra-Allreduce" => 1,
		       "intra-Reduce" => 1,
		       "intra-Scan" => 1,
		       "intra-Exscan" => 1,
		       "intra-Reduce_scatter" => 1,
		       "intra-Bcast" => 1,
		       
		       );
# Additional special cases
#		       "comm-Recv" => 1,  (before status)
#                      "comm-Sendrecv" , "comm->Sendrecv_replace"
#                      "dtype-Pack", "dtype-Unpack",


while (<>) {
    s/#.*//g;
    if (!/\S/) { next; }
    if (/<(\w*)>/) {
	print $_;
	next;
    }
    
    $isStatic = "";
    $isConst  = "";
    if (/static/) {
	s/static\s*//;
	$isStatic="static ";
    }
    s/MPI:://g;
    if (/const\s*$/) {
	s/const\s*$//;
	$isConst = " const";
    }
    if (/([\w:]*)\s+(\w*)::(\w*)\((.*)\)/) {
	$returnType = $1;
	$class      = $2;
	$routine    = $3;
	$args       = $4;
    }
    elsif (/([\w:]*)\s+(\w*)\((.*)\)/) {
	$returnType = $1;
	$class      = "base";
	$routine    = $2;
	$args       = $3;
    }
    else {
	print STDERR "Cannot categorize $_";
    }
    # Check for routines to skip because they are special
    if (defined($SkipRoutines{"$class-$routine"})) { 
	next;
    }

    $origArgs = $args;
    # Check for a duplicate defnition with no Status
    if (defined($KnownRoutines{"$class-$routine"})) {
	$oldargs = $KnownRoutines{"$class-$routine"};
	if ( ($oldargs =~ /Status/) && ! ($args =~ /Status/) ) {
	    # Skip this entry (handled automatically by buildiface)
	    next;
	}
	else {
	    print STDERR "$class-$routine has multiple definitions\n";
	    print STDERR "First:  $routine($oldargs)\n";
	    print STDERR "Second: $routine($args)\n";
	    next;
	}
    }
    $KnownRoutines{"$class-$routine"} = $origArgs;

    # Clean out the args
    $cleanargs = "";
    # remove the last ), and make it either a , or a blank.
    # the rule in the following is that args end in a ,
    $args =~ s/\)//;
    if ($args =~ /\S/) { $args = $args . ","; }
    # Split into single args to match entire argument
    foreach $arg (split(/,/,$args)) {
	$argConst = 0;
	$arg =~ s/^\s*//;
	$arg =~ s/\s*$//g;
	if ($arg =~ /^const\s/) {
	    $argConst = 1;
	    $arg =~ s/^const\s*//g;
	}
	$arg =~ s/^int\s+\w*$//g;
	$arg =~ s/^Aint\s+\w*$//g;
	$arg =~ s/^int\s+\w*\s*\[\]\s*//g;
	$arg =~ s/^Aint\s+\w*\s*\[\]\s*//g;
	$arg =~ s/^int\s*&\s*\w*$/in:refint/g;
	$arg =~ s/^Aint\s*&\s*\w*$//g;    # Hope that the automatic
	                                  # handling of * -> & works
	$arg =~ s/^Offset\s*&\s*\w*$//g;  # Ditto for offset
	$arg =~ s/^Offset\s+\w*//g;
	$arg =~ s/Prequest\s+\w*\[\]\s*/in:preqarray:1/;
	$arg =~ s/Request\s+\w*\[\]\s*/in:reqarray:1/;
	$arg =~ s/Status\s+\w*\[\]\s*/out:statusarray:1/;
	$arg =~ s/bool\s+\w*\[\]\s*/in:boolarray:-2/;
	$arg =~ s/^Status\s*&\s*\w*//g;   # Status handled by buildiface
	$arg =~ s/void\s*\*\s*\w*//g;
	$arg =~ s/char\s*\*\s*\w*//g;
	$arg =~ s/Datatype\s+\w*\[\]/in:dtypearray:--count--/g;
	$arg =~ s/^bool\s+\w*/in:bool/g;
	$arg =~ s///g;
	
	if ($argConst) {
	    if ($arg =~ /\s/) {
		if ($arg =~ /(\w*)\s*&\s*\w*/) {
		    $arg = "in:constref:$1";
		}
		else {
		    if ($arg =~ /dtypearray/) {
			$arg =~ s/dtypearray/dtypeconstarray/;
		    }
		    else {
			$arg = "in:const-$arg";
		    }
		}
	    }
	    else {
		$arg = "in:const";
	    }
	}
	$cleanargs .= "$arg ,";
    }
    # ALL args are terminated by a ,.  Thus, 
    # in the case of a single arg, there is a , 
    # NO args is empty.
    $args = $cleanargs;

    # Most MPI functions have the class as the first arg:
    if ($isStatic eq "" && $class ne "base") {
	$fnchash = "$class-$routine";
	if (defined($FuncHasClassAtEnd{$fnchash})) {
	    $args .= ",";
	}
	else {
	    $args = "," . $args;
	}
    }
    
    # Most MPI functions that return an object in a class do so at the end
    if ($returnType ne "void" && $returnType ne "bool") {
	$args .= "return,";
    }
    # Trim off the last comma
    $args =~ s/,\s*$//;

    if (!defined($ShortClassName{$class})) {
	print STDERR "$class has no short name\n";
    }
    $sclass = $ShortClassName{$class};
    print "$sclass-$routine $isStatic$returnType ($args)$isConst\n";
}
